#include "ECharacter.h"
#include "EQuat.h"
#include "EMath.h"
#include "eCPP.h"

namespace engine
{
//------------------------------------------------------------
// ECharacterDesc
//------------------------------------------------------------
ECharacterDesc::ECharacterDesc() :
m_fWalkMinWantedMovingSpeed( 0.02f ),
m_fRunMinWantedMovingSpeed( 0.17f ),
m_fWalkMovingSpeed( 0.28f ),
m_fRunMovingSpeed( 0.28f )
{

}

//------------------------------------------------------------
bool ECharacterDesc::isValid() const 
{
  cTEST( EPhCharacterDesc::isValid() );
  cTEST( m_fWalkMinWantedMovingSpeed > 0.f );
  cTEST( m_fWalkMovingSpeed > 0.f );
  cTEST( m_fRunMinWantedMovingSpeed > 0.f );
  cTEST( m_fRunMovingSpeed > 0.f );
  return true;
}

//------------------------------------------------------------
// ECharacter
//------------------------------------------------------------
ECharacter::ECharacter( const string& sName, const ECharacterDesc& desc ) :
EEntity( EEntity::ENTITY_GROUP_CHARACTER, sName, desc.m_sModelFileName ),
EPhCharacter( desc ),
m_Desc( desc ),
m_vMovingDirection( EVector::ZERO ),
m_fMovingSpeed( 0.f ),
m_vWantedMovingDirection( EVector::ZERO ),
m_fWantedMovingSpeed( 0.f ),
m_bIsMovingRequested( false ),
m_fYaw( 0.f ),
m_fRequestedYaw( 0.f )
{
  cASSERT( m_Desc.isValid() );
}

//------------------------------------------------------------
void ECharacter::move(const EVector& vNormalizedDirection, float fSpeed )
{
  if( fSpeed >= m_Desc.m_fRunMinWantedMovingSpeed )
  {
    m_fWantedMovingSpeed = m_Desc.m_fRunMovingSpeed;
    setAnimationState("Walk");
    enableAnimation( true );
  }
  else if( fSpeed >= m_Desc.m_fWalkMinWantedMovingSpeed )
  {
    m_fWantedMovingSpeed = m_Desc.m_fWalkMovingSpeed;
    setAnimationState("Stealth");
    enableAnimation( true );
  }
  else
  {
    m_fWantedMovingSpeed = 0.f;
  }
  m_vWantedMovingDirection  = vNormalizedDirection;
  m_vMovingDirection        = m_vWantedMovingDirection;
  if( m_fWantedMovingSpeed > 0.f )
  {
    m_bIsMovingRequested = true;
  }
  faceDirection( m_vMovingDirection );
}

//------------------------------------------------------------
void ECharacter::stopMoving()
{
  m_bIsMovingRequested      = false;
  m_bAnimationNotFinished   = true;
  m_fWantedMovingSpeed      = 0.f;
  AnimationState* pAnimationState = getAnimationState();
  pAnimationState->setLoop( false );
  setPosition( getPosition() );
}

//------------------------------------------------------------
void ECharacter::updateMove(const EGameTime& time)
{
  float fMoveCoef = 0.1f * time.m_fDT60;
  EMath::clampMax( fMoveCoef, 1.f );
  m_fMovingSpeed += (m_fWantedMovingSpeed - m_fMovingSpeed) * fMoveCoef;
}

//------------------------------------------------------------
void ECharacter::tryIfTheMovingAnimationHasEnded()
{
  AnimationState* pAnimationState = getAnimationState();
  cASSERT( pAnimationState != NULL );
  if( pAnimationState->hasEnded() )
  {
    m_fWantedMovingSpeed      = 0.f;
    m_fMovingSpeed            = 0.f;
    setAnimationState( "Idle2" );
    enableAnimation( true );
    m_bAnimationNotFinished = false;
  }
}

//------------------------------------------------------------
void ECharacter::faceDirection(const EVector& vDirection )
{
  m_fRequestedYaw = EMath::fastArcCos( -vDirection.z );
  if( vDirection.x >= 0 )
    m_fRequestedYaw = -m_fRequestedYaw;
}

//------------------------------------------------------------
void ECharacter::updateFacingDirection(const EGameTime& time)
{
  if( m_fYaw != m_fRequestedYaw )
  {
    float fDiff = EMath::smallestAngle( m_fYaw, m_fRequestedYaw );
    float fCoef = 0.2f * time.m_fDT60;
    EMath::clampMax( fCoef, 1.f );
    m_fYaw += fDiff * fCoef;
    m_pOgreNode->setOrientation(1.f, 0.f, 0.f, 0.f);
    m_pOgreNode->yaw( Radian( m_fYaw ) );
  }
}

//------------------------------------------------------------
bool ECharacter::update(const EGameTime& time)
{
  cTEST( EEntity::update( time ) );
  return true;
}

//------------------------------------------------------------
bool ECharacter::startGame()
{
  cTEST( EEntity::startGame() );
  return true;
}

//------------------------------------------------------------
bool ECharacter::updateGame(const EGameTime& time )
{
  cTEST( EEntity::updateGame( time ) );
  if( m_pPhController != NULL )
  {
    m_vPosition = m_pPhController->getPosition();
  }
  if( !m_bIsMovingRequested && m_bAnimationNotFinished )
    tryIfTheMovingAnimationHasEnded();
  updateMove( time );
  if( m_fMovingSpeed > 0.0001f )
  {
    EVector vPos = getPosition();
  }
  if( m_pPhController != NULL )
  {
    EVector vDisp = m_vMovingDirection * m_fMovingSpeed * time.m_fDT60;
    m_pPhController->move( vDisp, COLLISION_MULTI_GROUP_CHARACTER_COLLIDABLE );
  }
  setPosition(m_vPosition.x, m_vPosition.y, m_vPosition.z);
  updateFacingDirection( time );
  
  return true;
}

//------------------------------------------------------------
bool ECharacter::endGame()
{
  cTEST( EEntity::endGame() );
  return true;
}

} // end namespace